use super::{
    gather::do_gather, json, util::get_state, Plc, RNull, SvCache, Value, ST_ACTIVE, ST_DOING,
    ST_DONE, ST_WAIT,
};
use tracing::{instrument, warn};

#[instrument(skip_all, name = "scan_trigger")]
pub async fn scan_trigger(plc: Plc, real: SvCache, st_id: String) -> RNull {
    let state = get_state(&st_id, &real);

    let is_trigger = do_trigger(&plc, &real, &st_id, state).await?;

    if !state_ok(&st_id, &real, is_trigger, state).await {
        return Ok(());
    }

    match do_gather(&plc, &real, false).await {
        Ok(_) => {
            real.insert(st_id, json!(ST_DONE)).await;
        }
        Err(e) => {
            warn!(target: "处理信号失败", "gather error: {:?}", e);
            real.insert(st_id, json!(ST_WAIT)).await;
        }
    }

    Ok(())
}

async fn do_trigger(plc: &Plc, real: &SvCache, st_id: &str, state: i64) -> Result<bool, Value> {
    if state == ST_DONE || state == ST_WAIT {
        real.insert(st_id.to_string(), json!(ST_DOING)).await;
    }

    let ret = plc.read_trigger(&real).await;

    real.insert(st_id.to_string(), json!(state)).await;

    ret
}

#[instrument(skip_all, name = "state_ok")]
async fn state_ok(id: &str, cache: &SvCache, is_trigger: bool, state: i64) -> bool {
    warn!("cur trigger {:?}: {:?}, plc: {:?}", id, state, is_trigger);

    if !is_trigger {
        if state == ST_DONE {
            cache.insert(id.to_string(), json!(ST_WAIT)).await;
        }

        return false;
    }

    if state == ST_WAIT {
        cache.insert(id.to_string(), json!(ST_ACTIVE)).await;
        return true;
    }

    if state == ST_DONE {
        return false;
    }

    cache.insert(id.to_string(), json!(ST_ACTIVE)).await;

    true
}
